/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package be.vdab.voertuigen;

import be.vdab.util.Datum;
import be.vdab.util.DatumException;
import be.vdab.util.mens.Mens;
import be.vdab.util.mens.MensException;
import be.vdab.util.mens.Rijbewijs;
import be.vdab.voertuigen.div.Nummerplaat;
import java.io.Serializable;
import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
import java.util.Set;
import java.util.TreeSet;
import org.apache.commons.collections.CollectionUtils;


/**
 *
 * @author Joris.Lefever
 */
public abstract class Voertuig extends Throwable implements Comparable, Serializable {
    
    private final Nummerplaat nummerplaat = Nummerplaat.getNummerplaat();
    private String merk;
    private Datum datumEersteIngebruikname;
    private int aankoopprijs;
    private final int zitplaatsen;
    private Mens bestuurder;
    private Set<Mens> ingezetenen = new TreeSet<>();
    
    public Voertuig(String merk,Datum datum, int aankoopprijs, int zitplaatsen, Mens bestuurder,Mens... ingezetenen) {
        //setBestuurder(bestuurder);
        this.heeftToegestaneRijbewijzen(bestuurder);
        this.bestuurder = bestuurder;
        
        this.ingezetenen.add(bestuurder);
        this.ingezetenen.addAll(Arrays.asList(ingezetenen));
        if (zitplaatsen < 1) {
            throw new IllegalArgumentException();
        }
        if (this.ingezetenen.size() > zitplaatsen) {
            throw new MensException();
        } 
        
        
        this.merk = merk;
        this.datumEersteIngebruikname = datum;
        this.aankoopprijs = aankoopprijs;
        this.zitplaatsen = zitplaatsen;
    }
    
    
    protected abstract Rijbewijs[] getToegestaneRijbewijzen();
    
    protected final void heeftToegestaneRijbewijzen(Mens bestuurder){
        if (bestuurder.getRijbewijs()==null)
            throw new MensException();
        else if (!CollectionUtils.containsAny( Arrays.asList(getToegestaneRijbewijzen()) ,Arrays.asList(bestuurder.getRijbewijs()))){
            throw new MensException();
        }
    }

    public String getMerk() {
        return this.merk;
    }

    public void setMerk(String merk) {
        this.merk = merk;
    }

    public Datum getDatumEersteIngebruikname() {
        return this.datumEersteIngebruikname;
    }

    public void setDatumEersteIngebruikname(Datum eersteInGebruikName) throws DatumException {
        if (eersteInGebruikName.checkDatum(eersteInGebruikName.getDag(),eersteInGebruikName.getMaand(),eersteInGebruikName.getJaar())){
            throw new DatumException("Foute datum");
        } else {
            this.datumEersteIngebruikname = eersteInGebruikName;
        }
    }
    
    public int getAankoopprijs() {
        return this.aankoopprijs;
    }

    public void setAankoopprijs(int aankoopprijs) {
        this.aankoopprijs = aankoopprijs;
    }

    public Mens getBestuurder() {
        return this.bestuurder;
    }

    public void setBestuurder(Mens nieuweBestuurder) {
        if (this.ingezetenen.size()>= this.zitplaatsen && !this.isIngezetene(nieuweBestuurder)){
            throw new MensException();
        } else {
            this.heeftToegestaneRijbewijzen(nieuweBestuurder);
            this.ingezetenen.add(nieuweBestuurder);
            this.bestuurder = nieuweBestuurder;
        }
    }

    public Set<Mens> getIngezetenen() {
        return this.ingezetenen;
    }

    public void setIngezetenen(Set<Mens> inzittende) {
        this.ingezetenen = inzittende;
    }
    
    public Nummerplaat getNummerplaat(){
        return this.nummerplaat;
    }
    
    public Collection<Mens> getIngezeteneExclusiefBestuurder(){
        return CollectionUtils.subtract(this.ingezetenen, Arrays.asList(getBestuurder()));
        }
    
    public void addIngezetene(Mens nieuweIngezetenen){
        if (this.ingezetenen.size() == this.zitplaatsen && !this.isIngezetene(nieuweIngezetenen)){
            throw new MensException();
        } else {
            this.ingezetenen.add(nieuweIngezetenen);
        }
    }
    
    public boolean isIngezetene(Mens nieuweIngezetenen){
       if (CollectionUtils.containsAny( this.ingezetenen ,Arrays.asList(nieuweIngezetenen)))
           return true;
       else
           return false;
    }
    
    public static Comparator<Voertuig> getMerkComparator() {
        return new MerkComparator();
    }

    public static Comparator<Voertuig> getAankoopprijsComparator() {
        return new AankoopprijsComparator();
    }

    private static class MerkComparator implements Comparator<Voertuig>{
        @Override
        public int compare(Voertuig v1, Voertuig v2){
        if (v1.getMerk().compareTo(v2.getMerk()) == 0 )
                return 0 ;
            else
                if (v1.getMerk().compareTo(v2.getMerk()) > 0 )
                    return 1;
                else
                    return -1;   
        }
    }

    private static class AankoopprijsComparator implements Comparator<Voertuig>{
        @Override
        public int compare(Voertuig v1, Voertuig v2){
        if (v1.getAankoopprijs() == v2.getAankoopprijs() )
                return 0 ;
            else
                if (v1.getAankoopprijs() > v2.getAankoopprijs() )
                    return 1;
                else
                    return -1;   
        }
    }
            
        
    
    
    
    
    @Override
    public String toString(){
        String string = String.format("%s %s %s %s %s", nummerplaat.getPlaat(), this.merk, this.datumEersteIngebruikname.toString(), this.aankoopprijs,this.bestuurder.toString());
        if (getIngezeteneExclusiefBestuurder().isEmpty())
            return string;
        else 
            return new StringBuilder(string).append(String.format("%s %s", "",this.getIngezeteneExclusiefBestuurder().toString())).toString();
    }
    
    @Override
    public boolean equals(Object o){
         if (o==null)
            return false;
         else
            if (o instanceof Voertuig){
                Voertuig v = (Voertuig) o;
                if (nummerplaat.getPlaat().equals(v.getNummerplaat().getPlaat()))
                   return true;
                else
                   return false;
            }
            else
               return false;

    }
    
    @Override
    public int hashCode(){
       return nummerplaat.hashCode();
    }
    
    @Override
    public int compareTo(Object o) {
        if (o == null) {
            throw new NullPointerException();
        } else {
            Voertuig v = (Voertuig) o;
            if (nummerplaat.getPlaat().compareTo(v.getNummerplaat().getPlaat()) > 0) {
                return 1;
            } else {
                if (nummerplaat.getPlaat().compareTo(v.getNummerplaat().getPlaat()) < 0) {
                    return -1;
                } else {
                    return 0;
                }
            }
        }
    }
}
